using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using Voodoo.Sauce.Internal.Analytics;
using Voodoo.Sauce.Internal.RemoteConfig;
using Voodoo.Tune.Core;

namespace Voodoo.Sauce.Internal.VoodooTune
{
	public static class VoodooTuneManager
	{
		private const string TAG = "VoodooTuneManager";

		private const int DEFAULT_INIT_TIMEOUT_IN_MILLISECONDS_WITHOUT_CACHE = 3000;

		private static readonly VoodooTuneConfigurationManager _configurationManager = new VoodooTuneConfigurationManager();

		private static string _initError;

		private static bool _isTimeout;

		private static string _initFinishedMessage;

		private static bool _initFinished = false;

		internal static VoodooTuneInitAnalyticsInfoLog LastVoodooTuneInitInfo;

		private static VoodooTuneAbTestsTracker abTestTracker;

		private static bool HasCache => !string.IsNullOrEmpty(VoodooTunePersistentData.SavedConfig);

		private static bool IsAbTestValid => _configurationManager.VoodooConfig?.IsValid ?? false;

		public static int CurrentTimeoutInMilliseconds { get; private set; }

		private static double CurrentTimeoutInSeconds => Convert.ToDouble((float)CurrentTimeoutInMilliseconds / 1000f);

		public static async void Initialize(Action initFinishedEvent)
		{
			if (_initFinished)
			{
				_initFinishedMessage = _initError;
				initFinishedEvent?.Invoke();
			}
			else
			{
				DebugVTManager.Initialize();
				abTestTracker = new VoodooTuneAbTestsTracker();
				_initFinished = true;
			}
		}

		private static void SetTimeout()
		{
			RemoteConfigConfiguration itemOrDefault = GetItemOrDefault<RemoteConfigConfiguration>();
			CurrentTimeoutInMilliseconds = (HasCache ? Math.Max(0, itemOrDefault.InitTimeoutInMillisecondsWithCache) : 3000);
			VoodooLog.LogDebug(Module.VOODOO_TUNE, "VoodooTuneManager", $"Has cache: {HasCache}, Init with current timeout -> {CurrentTimeoutInMilliseconds}ms");
		}

		private static async Task<Task<ConfigResponse>> SendLoadingConfigurationRequest()
		{
			Task<ConfigResponse> configRequest = _configurationManager.LoadConfiguration();
			if (CurrentTimeoutInMilliseconds == 0)
			{
				VoodooLog.LogDebug(Module.VOODOO_TUNE, "VoodooTuneManager", "No need to wait for the Init response");
				_isTimeout = true;
				return configRequest;
			}
			CancellationTokenSource cts = new CancellationTokenSource(CurrentTimeoutInMilliseconds);
			while (!cts.IsCancellationRequested && !configRequest.IsCompleted)
			{
				await Task.Yield();
			}
			if (cts.IsCancellationRequested)
			{
				CancelInitRequest();
			}
			return configRequest;
		}

		private static void CancelInitRequest()
		{
			_isTimeout = true;
			_initFinishedMessage = "VoodooTune TimeOut";
			VoodooLog.LogDebug(Module.VOODOO_TUNE, "VoodooTuneManager", $"{_initFinishedMessage} - Cancelling VT init after {CurrentTimeoutInSeconds}s timeout");
			UpdateLastInitInfoAsTimeout();
		}

		private static void SaveConfiguration(Task<ConfigResponse> configRequest)
		{
			ConfigResponse result = configRequest.Result;
			if (!_isTimeout)
			{
				_initFinishedMessage = result.Error;
			}
		}

		private static void SaveConfiguration(string url, string response, string error, long responseCode, double durationInMilliseconds, ulong payloadSize)
		{
			var (text, deserializationTime) = ManageResponse(response, responseCode, error);
			VoodooLog.LogDebug(Module.VOODOO_TUNE, "VoodooTuneManager", $"Request duration: {durationInMilliseconds}ms {text}");
			VoodooTunePersistentData.SavedURL = url;
			LastVoodooTuneInitInfo = new VoodooTuneInitAnalyticsInfoLog(new VoodooTuneInitAnalyticsInfo
			{
				HttpResponseCode = responseCode,
				DurationInMilliseconds = durationInMilliseconds,
				HasCache = HasCache,
				HasTimeout = _isTimeout,
				FormatIssue = !IsAbTestValid,
				Timeout = CurrentTimeoutInMilliseconds,
				PayloadSize = payloadSize,
				DeserializationTime = deserializationTime
			}, response, error, text);
		}

		private static (string message, long? deserializationDuration) ManageResponse(string response, long responseCode, string error)
		{
			long? item = null;
			string text;
			if (!string.IsNullOrEmpty(response) && responseCode == 200)
			{
				text = "Init succeeded - ";
				if (_isTimeout)
				{
					text = text + "Save config for the next session: " + response;
					_configurationManager.SaveConfig(response);
				}
				else
				{
					text = text + "Save and load config: " + response;
					Stopwatch stopwatch = Stopwatch.StartNew();
					_configurationManager.SaveAndRefreshConfig(response);
					stopwatch.Stop();
					item = stopwatch.ElapsedMilliseconds;
					VoodooLog.LogDebug(Module.VOODOO_TUNE, "VoodooTuneManager", $"Save and load config duration: {stopwatch.ElapsedMilliseconds}ms");
					if (!IsAbTestValid)
					{
						text += " (the AB tests are misconfigured, please check the AB tests and the cohorts)";
					}
				}
				TrackAbTestModifications(response);
			}
			else if (error != null)
			{
				_initError = error;
				text = "Init failed with error: " + error;
			}
			else if (responseCode == 204)
			{
				_initError = "no configuration for this app";
				text = "Init failed: " + _initError;
			}
			else
			{
				_initError = $"responseCode: {responseCode} response: {response}";
				text = "Init failed: " + _initError;
			}
			return (message: text, deserializationDuration: item);
		}

		private static void TrackAbTestModifications(string response)
		{
			Voodoo.Tune.Core.VoodooConfig voodooConfig = _configurationManager.GetVoodooConfig(response);
			if (voodooConfig != null)
			{
				abTestTracker.TrackAbTestModifications(voodooConfig);
			}
		}

		private static void UpdateLastInitInfoAsTimeout()
		{
			LastVoodooTuneInitInfo = new VoodooTuneInitAnalyticsInfoLog(new VoodooTuneInitAnalyticsInfo
			{
				HttpResponseCode = -1L,
				DurationInMilliseconds = CurrentTimeoutInMilliseconds,
				HasCache = HasCache,
				HasTimeout = _isTimeout
			}, "", _initFinishedMessage, _initFinishedMessage);
		}

		public static List<T> GetSubclassesItems<T>() where T : class, new()
		{
			return _configurationManager.GetSubclassesItems<T>();
		}

		public static List<T> GetItems<T>() where T : class, new()
		{
			return _configurationManager.GetItems<T>();
		}

		public static List<T> GetItemsOrDefaults<T>() where T : class, new()
		{
			return _configurationManager.GetItemsOrDefaults<T>();
		}

		public static T GetItem<T>() where T : class, new()
		{
			return _configurationManager.GetItem<T>();
		}

		public static T GetItemOrDefault<T>() where T : class, new()
		{
			return _configurationManager.GetItemOrDefault<T>();
		}

		public static string GetConfigurationId()
		{
			return _configurationManager?.VoodooConfig?.ConfigurationId;
		}

		public static string GetSegmentationUuid()
		{
			return _configurationManager?.VoodooConfig?.SegmentIds;
		}

		public static List<string> GetSegmentationUuidsAsList()
		{
			return _configurationManager?.VoodooConfig?.SegmentIdsToList ?? new List<string>();
		}

		public static string GetMainAbTestUuid()
		{
			return _configurationManager?.VoodooConfig?.MainAbTestId;
		}

		public static List<string> GetAbTestUuidsAsList()
		{
			return _configurationManager?.VoodooConfig?.AbTestIdsToList ?? new List<string>();
		}

		public static string GetAbTestUuids()
		{
			return _configurationManager?.VoodooConfig?.AbTestIds;
		}

		public static string GetMainAbTestCohortName()
		{
			return _configurationManager?.VoodooConfig?.MainCohortName;
		}

		public static string GetMainAbTestCohortUuid()
		{
			return _configurationManager?.VoodooConfig?.MainCohortId;
		}

		public static List<string> GetAbTestCohortUuidsAsList()
		{
			return _configurationManager?.VoodooConfig?.CohortIdsToList ?? new List<string>();
		}

		public static string GetAbTestCohortUuids()
		{
			return _configurationManager?.VoodooConfig?.CohortIds;
		}

		public static string GetAbTestVersionUuid()
		{
			return _configurationManager?.VoodooConfig?.VersionNumber;
		}

		public static bool GetDebuggerAuthorization()
		{
			return (_configurationManager?.VoodooDebug?.Authorized).GetValueOrDefault();
		}

		public static string GetRawConfiguration()
		{
			return _configurationManager?.RawConfig;
		}

		public static Dictionary<string, string> GetItemsJson()
		{
			return _configurationManager.GetJsonConfigurations();
		}
	}
}
